#include "Mepa.h"

//#define DEBUG
#define INI_ENDERECO   0
#define TAM_ENDERECO   5
#define INI_INSTRUCAO  6
#define TAM_INSTRUCAO  4
#define INI_ARGUMENTOS 11
#define TAM_ARGUMENTOS 18
#define INI_COMENTARIO 30

Mepa::Mepa(char *fileEntrada, char *saida, int tamanhoPrograma, int tamanhoMemoria, int tamanhoRegistrador)
{
    if (saida[0] == 0)
    {
        std::cout << "\n           D U M P   D E   M E M O R I A           \n"; 
        this->mGerarArquivo = false;
    }
    else
    {
        strcpy(this->mFileSaida, saida);
        std::ofstream fileSaida(saida, std::ios::out);
        this->mGerarArquivo = true;
        
        fileSaida << "\n           D U M P   D E   M E M O R I A           \n"; 
        fileSaida.close();
    }

    this->mFileEntrada.open(fileEntrada);
    
    if (this->mFileEntrada == false)
    {
        std::cout << "--------------------------------------------------" << std::endl;
        std::cout << "                         E R R O !                " << std::endl;
        std::cout << " Nao foi possivel encontrar o arquivo: " << fileEntrada << std::endl;
        std::cout << "--------------------------------------------------" << std::endl;
        exit(1);
    }

    this->numLinhas           = 0;
    this->mTamanhoMemoria     = (tamanhoMemoria     == -1 ? 256 : tamanhoMemoria);
    this->mTamanhoRegistrador = (tamanhoRegistrador == -1 ? 256 : tamanhoRegistrador);
    this->mTamanhoPrograma    = (tamanhoPrograma    == -1 ? 256 : tamanhoPrograma);

    this->mMemoria            = new int [this->mTamanhoMemoria];
    this->mRegistrador        = new int [this->mTamanhoRegistrador];
    this->mPrograma           = new MepaLinha[this->mTamanhoPrograma];

    for (int i = 0; i < this->mTamanhoRegistrador; i++)
        this->mRegistrador[i] = -1;


    this->mTopoMemoria        = -1;
    this->mProximaInstrucao   = 0;

    
    
    this->carregarCodigo();
}

Mepa::~Mepa()
{
    if (this->mFileEntrada) this->mFileEntrada.close();
}

void Mepa::setTopoMemoria(int valor)
{
    if (valor < -2 || valor >= this->mTamanhoMemoria)
    {
        std::cout << "--------------------------------------------------" << std::endl;
        std::cout << "                         E R R O !                " << std::endl;
        std::cout << " Acesso de endereco invalido na memoria de dados  " << std::endl;
        std::cout << "--------------------------------------------------" << std::endl;
        exit(1);
    }

    this->mTopoMemoria = valor;
}

void Mepa::setProximaInstrucao(int valor)
{
    if (valor < 0 || valor >= this->mTamanhoPrograma)
    {
        std::cout << "--------------------------------------------------" << std::endl;
        std::cout << "                         E R R O !                " << std::endl;
        std::cout << " Acesso de endereco invalido na pilha de programa " << std::endl;
        std::cout << "--------------------------------------------------" << std::endl;
        exit(1);
    }

    this->mProximaInstrucao = valor;
}

void Mepa::executar()
{
    MepaLinha linhaAtual = this->mPrograma[this->mProximaInstrucao];

    while (strcmp(linhaAtual.instrucao, "PARA") != 0)
    {
        if      (strcmp(linhaAtual.instrucao, "INPP") == 0) this->INPP();

        else if (strcmp(linhaAtual.instrucao, "AMEM") == 0) this->AMEM(atoi(linhaAtual.parametro1));

        else if (strcmp(linhaAtual.instrucao, "DMEM") == 0) this->DMEM(atoi(linhaAtual.parametro1));

        else if (strcmp(linhaAtual.instrucao, "ARMZ") == 0) this->ARMZ(atoi(linhaAtual.parametro1), atoi(linhaAtual.parametro2));
        
        else if (strcmp(linhaAtual.instrucao, "MOSM") == 0) this->MOSM();
        
        else if (strcmp(linhaAtual.instrucao, "DSVS") == 0) this->DSVS(linhaAtual.parametro1);

        else if (strcmp(linhaAtual.instrucao, "DSVF") == 0) this->DSVF(linhaAtual.parametro1);

        else if (strcmp(linhaAtual.instrucao, "NADA") == 0) this->NADA();
        
        else if (strcmp(linhaAtual.instrucao, "IMPL") == 0) this->IMPL();

        else if (strcmp(linhaAtual.instrucao, "IMPC") == 0) this->IMPC();

        else if (strcmp(linhaAtual.instrucao, "IMPR") == 0) this->IMPR();

        else if (strcmp(linhaAtual.instrucao, "LEIT") == 0) this->LEIT();

        else if (strcmp(linhaAtual.instrucao, "CRCT") == 0) this->CRCT(atoi(linhaAtual.parametro1));

        else if (strcmp(linhaAtual.instrucao, "CRVL") == 0) this->CRVL(atoi(linhaAtual.parametro1), atoi(linhaAtual.parametro2));

        else if (strcmp(linhaAtual.instrucao, "SOMA") == 0) this->SOMA();

        else if (strcmp(linhaAtual.instrucao, "SUBT") == 0) this->SUBT();

        else if (strcmp(linhaAtual.instrucao, "MULT") == 0) this->MULT();

        else if (strcmp(linhaAtual.instrucao, "DIVI") == 0) this->DIVI();

        else if (strcmp(linhaAtual.instrucao, "INVR") == 0) this->INVR();

        else if (strcmp(linhaAtual.instrucao, "CONJ") == 0) this->CONJ();

        else if (strcmp(linhaAtual.instrucao, "DISJ") == 0) this->DISJ();

        else if (strcmp(linhaAtual.instrucao, "NEGA") == 0) this->NEGA();

        else if (strcmp(linhaAtual.instrucao, "CMME") == 0) this->CMME();

        else if (strcmp(linhaAtual.instrucao, "CMMA") == 0) this->CMMA();

        else if (strcmp(linhaAtual.instrucao, "CMIG") == 0) this->CMIG();

        else if (strcmp(linhaAtual.instrucao, "CMDG") == 0) this->CMDG();

        else if (strcmp(linhaAtual.instrucao, "CMEG") == 0) this->CMEG();

        else if (strcmp(linhaAtual.instrucao, "CMAG") == 0) this->CMAG();
        
        else if (strcmp(linhaAtual.instrucao, "CHPR") == 0) this->CHPR(linhaAtual.parametro1);
        
        else if (strcmp(linhaAtual.instrucao, "ENPR") == 0) this->ENPR(atoi(linhaAtual.parametro1));
        
        else if (strcmp(linhaAtual.instrucao, "RTPR") == 0) this->RTPR(atoi(linhaAtual.parametro1), atoi(linhaAtual.parametro2));
        
        else if (strcmp(linhaAtual.instrucao, "CRVI") == 0) this->CRVI(atoi(linhaAtual.parametro1), atoi(linhaAtual.parametro2));
        
        else if (strcmp(linhaAtual.instrucao, "ARMI") == 0) this->ARMI(atoi(linhaAtual.parametro1), atoi(linhaAtual.parametro2));
        
        else if (strcmp(linhaAtual.instrucao, "CREN") == 0) this->CREN(atoi(linhaAtual.parametro1), atoi(linhaAtual.parametro2));
        
        else
        {
            std::cout << "--------------------------------------------------" << std::endl;
            std::cout << "                         E R R O !                " << std::endl;
            std::cout << " " << linhaAtual.instrucao << ": Instrucao desconhecida." << std::endl;
            std::cout << "--------------------------------------------------" << std::endl;
            exit(1);
        }

        linhaAtual = mPrograma[this->mProximaInstrucao];
    }
    std::cout << std::endl;
    std::cout << "--------------------------------------------------" << std::endl;
    std::cout << " Programa executado com sucesso. " << std::endl;
    std::cout << "--------------------------------------------------" << std::endl;
}

void Mepa::carregarCodigo()
{
    char buffer[256];
    int  index = 0; 

    while (mFileEntrada.peek() != EOF && index++ <= this->mTamanhoPrograma)
    {
        this->mFileEntrada.getline(buffer, 256);
        this->parsearLinha(buffer, this->numLinhas++);

        for (int i = 0 ; i < 256; i++ ) buffer[i] = 0;
    }
}

void Mepa::alocaParametro(struct MepaLinha *linha, char *parametro)
{
    if (strlen(parametro) == 0) return ;

    if (linha->parametro1 == NULL)
    {
        linha->parametro1 = new char[6];
        strcpy(linha->parametro1, parametro);
    }
    else
    {
        linha->parametro2 = new char[6];
        strcpy(linha->parametro2, parametro);
    }
}

/*********************************************************************************************************************

    FORMATO DA LINHA DO MEPA

      0  0  0  0  5     A  R  M  Z     0  ,  0                                                  #  c  o  m  e  n ...
    |--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|-- ...
     00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 ...
    |-----------------|--------------|--------------------------------------------------------|----------------- ...
        endereco          instrucao     argumentos                                               comentario

*********************************************************************************************************************/

void Mepa::parsearLinha(char *buffer, int index)
{
    char rotulo[6];
    char parametro[6];
    
	// faz a leitura do rotulo ou endereco
    for (int i = INI_ENDERECO; i < INI_ENDERECO + TAM_ENDERECO && i < strlen(buffer); i++)
    	rotulo[i] = buffer[i];

    rotulo[TAM_ENDERECO] = '\0';
    
    for (int i = 4; rotulo[i] == ' '; i--)
        rotulo[i] = 0;

    mPrograma[index].endereco = index;

    // verifica se foi lido um rotulo
    if (atoi(rotulo) == 0 && strcmp(rotulo, "     ") != 0)
    {
        mPrograma[index].rotulo = new char[6];
        strcpy(mPrograma[index].rotulo, rotulo);
    }
    else
    {
        mPrograma[index].rotulo = NULL;
    }
    
    // faz a leitura da instrucao
    for (int i = INI_INSTRUCAO; i < INI_INSTRUCAO + TAM_INSTRUCAO && i < strlen(buffer); i++)
    	mPrograma[index].instrucao[i - INI_INSTRUCAO] = buffer[i];

    mPrograma[index].instrucao[TAM_INSTRUCAO] = '\0';

    mPrograma[index].parametro1               = NULL;
    mPrograma[index].parametro2               = NULL;

    int indice = 0;

	//faz a leitura dos parametros
    for (int i = INI_ARGUMENTOS; i < INI_ARGUMENTOS + TAM_ARGUMENTOS && i <= strlen(buffer); i++)
    {
        if (buffer[i] != ' ' && buffer[i] != '\t' && buffer[i] != '#' && buffer[i] != ',' && buffer[i] != '\r')
        {
            parametro[indice] = buffer[i];
            indice++;
        }
        else if (buffer[i] == ',')
        {
            parametro[indice] = '\0';
            alocaParametro(&mPrograma[index],parametro);
            indice = 0;
    	}
        else if (buffer[i] == ' ' || buffer[i] == '\t' || buffer[i] == '#' || buffer[i] == 0 || buffer[i] != '\r')
    	{
            parametro[indice] = '\0';
            alocaParametro(&mPrograma[index], parametro);
            indice = 0;
    	}
	}
    parametro[indice] = '\0';
    alocaParametro(&mPrograma[index], parametro);
}

void Mepa::formataImpressao(char * string, char caracter, int tamanho, char alinhamento)
{
    if (this->mGerarArquivo == false)
    {
        if (alinhamento == 'l')
            std::cout.setf(std::ios::left);
        else
            std::cout.setf(std::ios::right);
    
        std::cout.width(tamanho);
    
        std::cout.fill(caracter);
    
        std::cout << string;
    }
    else
    {
        std::ofstream fileSaida(this->mFileSaida, std::ios::app);
        if (alinhamento == 'l')
            fileSaida.setf(std::ios::left);
        else
            fileSaida.setf(std::ios::right);
    
        fileSaida.width(tamanho);
    
        fileSaida.fill(caracter);
    
        fileSaida << string;
    }
}

void Mepa::formataImpressao(int numero, char caracter, int tamanho, char alinhamento)
{
    if (this->mGerarArquivo == false)
    {
        if (numero < 0)
        {
            numero = - numero;
            std::cout << "-";
        }

        if (alinhamento == 'l')
            std::cout.setf(std::ios::left);
        else
            std::cout.setf(std::ios::right);
    
        std::cout.width(tamanho);

        std::cout.fill(caracter);

        std::cout << numero;
    }
    else
    {

        std::ofstream fileSaida(this->mFileSaida, std::ios::app);

        if (numero < 0)
        {
            numero = - numero;
            fileSaida << "-";
        }

    
        fileSaida.width(tamanho);

        fileSaida.fill(caracter);

        fileSaida << numero;
    }
}

void Mepa::formataImpressao(std::ofstream &fileSaida, int numero, char caracter, int tamanho, char alinhamento)
{
        if (numero < 0)
        {
            numero = - numero;
            fileSaida << "-";
        }

    
        fileSaida.width(tamanho);

        fileSaida.fill(caracter);

        fileSaida << numero;
}


// iniciar programa principal
// s = -1  D[0] = 0
void Mepa::INPP()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> INPP " << std::endl;
    #endif

    this->setTopoMemoria(-1);
    this->mRegistrador[0] = 0;
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// alocar memoria
// s := s + m
void Mepa::AMEM(int n)
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> AMEM " << n << std::endl;
    #endif

    this->setTopoMemoria(this->mTopoMemoria + n);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// desalocar memoria
// s := s – n
void Mepa::DMEM(int n)
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> DMEM " << n << std::endl;
    #endif

    this->setTopoMemoria(this->mTopoMemoria - n);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

void Mepa::MOSM()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> MOSM " << std::endl;
    #endif

    if (this->mGerarArquivo == false)
    {
        std::cout << " ============================================ " << std::endl;
    
        std::cout << " i: "; formataImpressao(this->mProximaInstrucao, '0', 5, 'r'); std::cout << std::endl;
        std::cout << " s: "; formataImpressao(this->mTopoMemoria, '0', 5, 'r');      std::cout << std::endl;
    
        for (int i = 0; i < this->mTamanhoRegistrador; i++)
        {
            std::cout << " D[" << i << "]: ";
            if (this->mRegistrador[i] >= 0)        
                formataImpressao(this->mRegistrador[i], '0', 5, 'r');
            else
                std::cout << "NULL";
            std::cout << std::endl;
        }
    
        std::cout << " --------------------------------------------" << std::endl;
    
        for (int i = 0; i <= this->mTopoMemoria; i++)
        {
            std::cout << " ["; formataImpressao(i, '0', 5, 'r'); std::cout << "]: "; formataImpressao(this->mMemoria[i], '0', 5, 'r'); std::cout << std::endl;
        }
    
        this->setProximaInstrucao(this->mProximaInstrucao + 1);
    }
    else
    {
        std::ofstream fileSaida(this->mFileSaida, std::ios::app);

        fileSaida << " ============================================ \n"; 
    
        fileSaida << " i: "; formataImpressao(fileSaida, this->mProximaInstrucao, '0', 5, 'r'); fileSaida << "\n";
        fileSaida << " s: "; formataImpressao(fileSaida, this->mTopoMemoria, '0', 5, 'r');      fileSaida << "\n";
    
        for (int i = 0; i < this->mTamanhoRegistrador; i++)
        {
            fileSaida << " D[" << i << "]: ";
            if (this->mRegistrador[i] >= 0)        
                formataImpressao(fileSaida, this->mRegistrador[i], '0', 5, 'r');
            else
                fileSaida << "NULL";
            fileSaida << "\n";
        }
    
        fileSaida << " --------------------------------------------\n";
    
        for (int i = 0; i <= this->mTopoMemoria; i++)
        {
            fileSaida << " ["; formataImpressao(fileSaida, i, '0', 5, 'r');
            fileSaida << "]: "; formataImpressao(fileSaida, this->mMemoria[i], '0', 5, 'r');
            fileSaida << "\n";
        }
    
        this->setProximaInstrucao(this->mProximaInstrucao + 1);
    }
}

// nada
void Mepa::NADA()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> NADA " << std::endl;
    #endif
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// imprimir caracter asc e iniciar nova linha
void Mepa::IMPC()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> IMPC " << std::endl;
    #endif

    std::cout << (char) this->mMemoria[this->mTopoMemoria] << std::endl;
    this->setTopoMemoria(this->mTopoMemoria - 1);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// imprimir e pular linha
void Mepa::IMPL()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> IMPL " << std::endl;
    #endif

    std::cout << this->mMemoria[this->mTopoMemoria] << std::endl;
    this->setTopoMemoria(this->mTopoMemoria - 1);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// imprimir
void Mepa::IMPR()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> IMPR " << std::endl;
    #endif

    std::cout << this->mMemoria[this->mTopoMemoria] << std::endl;
    this->setTopoMemoria(this->mTopoMemoria - 1);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}


// leitura
void Mepa::LEIT()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> LEIT " << std::endl;
    #endif

    this->setTopoMemoria(this->mTopoMemoria + 1);
    std::cin >> this->mMemoria[this->mTopoMemoria];
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// armazenar valor
// M[D[k]+n] := M[s];  s := s – 1
void Mepa::ARMZ(int k, int n)
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> ARMZ " << k << "," << n << std::endl;
    #endif

    this->mMemoria[this->mRegistrador[k] + n] = this->mMemoria[this->mTopoMemoria];
    this->setTopoMemoria(this->mTopoMemoria - 1);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

void Mepa::DSVS(char * p)
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> DSVS " << p << std::endl;
    #endif

    int index = 0;
    while (strcmp(p, this->mPrograma[index].rotulo) != 0)
        index++;

    this->setProximaInstrucao(index);
    // std::cout << "<<<<<<<<<<<<<<<<<<<<<<<<<<< " << index << " >>>>>>>>>>>>>>>>>>>>>>>>>>>>>" << std::endl;
}

void Mepa::DSVF(char * p)
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> DSVF " << p << std::endl;
    #endif

    if (this->mMemoria[this->mTopoMemoria] == 0)
    {
        int index = 0;
        while (strcmp(p, this->mPrograma[index].rotulo) != 0)
            index++;

        // std::cout << "<<<<<<<<<<<<<<<<<<<<<<<<<<< " << index << " >>>>>>>>>>>>>>>>>>>>>>>>>>>>>" << std::endl;    
   
        this->setProximaInstrucao(index);
    }
    else
    {
        this->setProximaInstrucao(this->mProximaInstrucao + 1);
    }
    this->setTopoMemoria(this->mTopoMemoria - 1);
}

// carrega constante
// s := s + 1;   M[s] := k
void Mepa::CRCT(int k)
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> CRCT " << k << std::endl;
    #endif

    this->setTopoMemoria(this->mTopoMemoria + 1);
    this->mMemoria[this->mTopoMemoria] = k;
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}


// carrega valor
// s := s + 1;  M[s] := M[D[k] + n]
void Mepa::CRVL(int k, int n)
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> CRVL " << k << "," << n << std::endl;
    #endif

    this->setTopoMemoria(this->mTopoMemoria + 1);
    this->mMemoria[this->mTopoMemoria] = this->mMemoria[this->mRegistrador[k] + n];
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// somar
// M[s-1] := M[s-1] + M[s];     s := s – 1
void Mepa::SOMA()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> SOMA " << std::endl;
    #endif

    this->mMemoria[this->mTopoMemoria - 1] += this->mMemoria[this->mTopoMemoria];
    this->setTopoMemoria(this->mTopoMemoria - 1);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// subtrair
// M[s-1] := M[s-1] - M[s];     s := s – 1
void Mepa::SUBT()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> SUBT " << std::endl;
    #endif

    this->mMemoria[this->mTopoMemoria - 1] -= this->mMemoria[this->mTopoMemoria];
    this->setTopoMemoria(this->mTopoMemoria - 1);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// multiplicar
// M[s-1] := M[s-1] * M[s];     s := s – 1
void Mepa::MULT()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> MULT " << std::endl;
    #endif

    this->mMemoria[this->mTopoMemoria - 1] *= this->mMemoria[this->mTopoMemoria];
    this->setTopoMemoria(this->mTopoMemoria - 1);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// dividir
// M[s-1] := M[s-1] div M[s];     s := s – 1
void Mepa::DIVI()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> DIVI " << std::endl;
    #endif

    this->mMemoria[this->mTopoMemoria - 1] /= this->mMemoria[this->mTopoMemoria];
    this->setTopoMemoria(this->mTopoMemoria - 1);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// inverter sinal
// M[s] := -M[s]
void Mepa::INVR()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> INVR " << std::endl;
    #endif

    this->mMemoria[this->mTopoMemoria] = - this->mMemoria[this->mTopoMemoria];
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// conjuncao
// se M[s-1]=1 e M[s]=1 
//    entao M[s-1] :=1   senao M[s-1] := 0
// s := s – 1
void Mepa::CONJ()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> CONJ " << std::endl;
    #endif

    if (this->mMemoria[this->mTopoMemoria - 1] == 1 && this->mMemoria[this->mTopoMemoria] == 1)
        this->mMemoria[this->mTopoMemoria - 1] = 1;
    else
        this->mMemoria[this->mTopoMemoria - 1] = 0;

    this->setTopoMemoria(this->mTopoMemoria - 1);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// disjuncao
//
// se M[s-1]=1 ou M[s]=1 
//    entao M[s-1] :=1   senao M[s-1] := 0
// s := s – 1
void Mepa::DISJ()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> DISJ " << std::endl;
    #endif

    if (this->mMemoria[this->mTopoMemoria - 1] == 1 || this->mMemoria[this->mTopoMemoria] == 1)
        this->mMemoria[this->mTopoMemoria - 1] = 1;
    else
        this->mMemoria[this->mTopoMemoria - 1] = 0;

    this->setTopoMemoria(this->mTopoMemoria - 1);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// negacao
// se M[s]=1-M[s]
void Mepa::NEGA()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> NEGA " << std::endl;
    #endif

    this->mMemoria[this->mTopoMemoria] = 1 - this->mMemoria[this->mTopoMemoria];
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// comparar menor
// se M[s-1] < M[s] 
//   então M[s-1]:=1  senão M[s-1]:= 0
// s := s – 1
void Mepa::CMME()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> CMME " << std::endl;
    #endif

    if (this->mMemoria[this->mTopoMemoria - 1] < this->mMemoria[this->mTopoMemoria])
        this->mMemoria[this->mTopoMemoria - 1] = 1;
    else
        this->mMemoria[this->mTopoMemoria - 1] = 0;
    
    this->setTopoMemoria(this->mTopoMemoria - 1);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// comparar maior
// se M[s-1] > M[s] 
//   então M[s-1]:=1  senão M[s-1]:= 0
// s := s – 1
void Mepa::CMMA()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> CMMA " << std::endl;
    #endif

    if (this->mMemoria[this->mTopoMemoria - 1] > this->mMemoria[this->mTopoMemoria])
        this->mMemoria[this->mTopoMemoria - 1] = 1;
    else
        this->mMemoria[this->mTopoMemoria - 1] = 0;
    
    this->setTopoMemoria(this->mTopoMemoria - 1);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// comparar igual
// se M[s-1] = M[s] 
//   então M[s-1]:=1  senão M[s-1]:= 0
// s := s – 1
void Mepa::CMIG()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> CMIG " << std::endl;
    #endif

    if (this->mMemoria[this->mTopoMemoria - 1] == this->mMemoria[this->mTopoMemoria])
        this->mMemoria[this->mTopoMemoria - 1] = 1;
    else
        this->mMemoria[this->mTopoMemoria - 1] = 0;
    
    this->setTopoMemoria(this->mTopoMemoria - 1);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// comparar desigual
// se M[s-1] <> M[s] 
//   então M[s-1]:=1  senão M[s-1]:= 0
// s := s – 1
void Mepa::CMDG()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> CMDG " << std::endl;
    #endif

    if (this->mMemoria[this->mTopoMemoria - 1] != this->mMemoria[this->mTopoMemoria])
        this->mMemoria[this->mTopoMemoria - 1] = 1;
    else
        this->mMemoria[this->mTopoMemoria - 1] = 0;
    
    this->setTopoMemoria(this->mTopoMemoria - 1);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// comparar menor ou igual
// se M[s-1] <= M[s] 
//   então M[s-1]:=1  senão M[s-1]:= 0
// s := s – 1
void Mepa::CMEG()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> CMEG " << std::endl;
    #endif

    if (this->mMemoria[this->mTopoMemoria - 1] <= this->mMemoria[this->mTopoMemoria])
        this->mMemoria[this->mTopoMemoria - 1] = 1;
    else
        this->mMemoria[this->mTopoMemoria - 1] = 0;
    
    this->setTopoMemoria(this->mTopoMemoria - 1);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// comparar maior ou igual
// se M[s-1] >= M[s] 
//   então M[s-1]:=1  senão M[s-1]:= 0
// s := s – 1
void Mepa::CMAG()
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> CMAG " << std::endl;
    #endif

    if (this->mMemoria[this->mTopoMemoria - 1] >= this->mMemoria[this->mTopoMemoria])
        this->mMemoria[this->mTopoMemoria - 1] = 1;
    else
        this->mMemoria[this->mTopoMemoria - 1] = 0;
    
    this->setTopoMemoria(this->mTopoMemoria - 1);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// chamar procedimento
// s:= s + 1; M[s]:= i +1 (*retorno*); I:= 'endereco de L'
void Mepa::CHPR(char * L)
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> CHPR " << L << std::endl;
    #endif

    int index = 0;
    while (strcmp(L, this->mPrograma[index].rotulo) != 0)
        index++;

    this->setTopoMemoria(this->mTopoMemoria + 1);
    this->mMemoria[this->mTopoMemoria] = this->mProximaInstrucao + 1;
    this->setProximaInstrucao(index);
}

// entrar procedimento – k: nivel procedimento
// s := s +1; M[s] := D[k]; D[k] := s +1
void Mepa::ENPR(int k)
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> ENPR " << k << std::endl;
    #endif

    this->setTopoMemoria(this->mTopoMemoria + 1);
    this->mMemoria[this->mTopoMemoria] = this->mRegistrador[k];
    this->mRegistrador[k]              = this->mTopoMemoria + 1;
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// retornar do procedimento – k: nivel lexico
// D[k] := M[s];  i := M[s-1];  s := s – (n+2)
void Mepa::RTPR(int k, int n)
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> RTPR " << k << "," << n << std::endl;
    #endif

    this->mRegistrador[k]   = this->mMemoria[this->mTopoMemoria];
    this->setProximaInstrucao(this->mMemoria[this->mTopoMemoria - 1]);
    this->setTopoMemoria(this->mTopoMemoria - (n + 2));
}

// carregar valor indireto
// s := s + 1;  M[s] := M[ M[D[k] + n] ]
void Mepa::CRVI(int k, int n)
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> CRVI " << k << "," << n << std::endl;
    #endif

    this->setTopoMemoria(this->mTopoMemoria + 1);
    this->mMemoria[this->mTopoMemoria] = this->mMemoria[ this->mMemoria[ this->mRegistrador[k] + n ] ];
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// armazenar valor
// M[ M[D[k]+n] ] := M[s];  s := s – 1
void Mepa::ARMI(int k, int n)
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> ARMI " << k << "," << n << std::endl;
    #endif

    this->mMemoria[ this->mMemoria[ this->mRegistrador[k] + n ] ] = this->mMemoria[this->mTopoMemoria];
    this->setTopoMemoria(this->mTopoMemoria - 1);
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

// carregar endereco
// s:= s + 1; M[s]:= D[k] + n
void Mepa::CREN(int k, int n)
{
    #ifdef DEBUG
        std::cout << " DEBUG ---> CREN " << k << "," << n << std::endl;
    #endif

    this->setTopoMemoria(this->mTopoMemoria + 1);
    this->mMemoria[this->mTopoMemoria] = this->mRegistrador[k] + n;
    this->setProximaInstrucao(this->mProximaInstrucao + 1);
}

